Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix key validator false negatives on empty collections #363

Merged

Conversation

Drenmi
Copy link
Contributor

@Drenmi Drenmi commented Jun 17, 2021

What was happening?

As described in #349 and #355, when using validate_keys = true, there are false negatives when the schema encounters unexpected keys where the value is an empty array or -hash, or an array with only primitives.

This was happening because the recursively defined KeyValidator#key_paths did not handle the cases where there was nothing to recurse over. (The correct behaviour would be to instantly return the key.)

The output of this method is what is subsequently checked against the key map, but with the relevant keys missing, the validation logic would overlook these unexpected keys.

How does this fix it?

  • When encountering an empty hash value, immediately add the key to the key paths.
  • When encountering an array value, return the key immediately if the array 1) has no nested collections, 2) all nested collections are empty, or 3) contains only primitives.

What is the impact?

This change affects only private APIs, but the obvious "side effect" is that the bug is fixed, and anyone whose application incidentally works because unexpected keys were ignored, might start seeing errors in their application after upgrading.

Limitations

There was a related, but separate, issue highlighted in #355. For nested hashes, the error is tacked onto the innermost hash, rather than on the first key that is unexpected. After having a quick look I decided to not address that in this same PR, as the changes are quite involved, and probably deserve their own PR.

@Drenmi Drenmi requested a review from solnic as a code owner June 17, 2021 06:59
hashes_or_arrays = value.select { |e| (e.is_a?(Array) || e.is_a?(Hash)) && !e.empty? }

next key.to_s if hashes_or_arrays.empty?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since it's skipping empy items now, aren't we going to end up with nils in the resulting array?

Copy link
Contributor Author

@Drenmi Drenmi Jun 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The next keyword optionally accepts one argument which will be taken by the iterator. In this case that would be key.to_s if there are no nested collections. 🙂

3.times.map { |n| next n }
#=> [0, 1, 2]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah doooh of course! Thanks for the explanation 😄

@solnic solnic merged commit 21ebe40 into dry-rb:master Jun 18, 2021
@Drenmi Drenmi deleted the bugfix/key-validator-on-empty-collections branch June 18, 2021 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants